package com.qrxl.common.service.impl;


import com.qrxl.common.exception.CustomException;
import com.qrxl.util.JGAssert;
import com.qrxl.enums.EnumUploadItem;
import com.qrxl.util.DateHandler;
import com.qrxl.util.StringUtil;
import org.apache.commons.lang3.StringUtils;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Base64;
import java.util.Date;

/**
 * 文件存储接口
 *
 * @author zhoukun
 */
public abstract class AbstractFileStoreServiceImpl {
    final String PATH_SPLIT = "/";
    final String PATH_PREFIX = "oss/upload/";

    public String uploadFile(EnumUploadItem enumUploadItem, File file) throws Exception {
        JGAssert.notNull(file, "file不能为空");
        return uploadFile(enumUploadItem, fileToByte(file));
    }

    public String uploadBase64(EnumUploadItem enumUploadItem, String base64) throws Exception {
        //base64标准有两种（换行、不换行），我们统一去掉换行
        base64 = base64.replaceAll("\\r", "").replaceAll("\\n", "");
        JGAssert.hasText(base64, "文件不能为空");
        return uploadFile(enumUploadItem, Base64.getDecoder().decode(base64));
    }

    public String uploadFile(EnumUploadItem enumUploadItem, byte[] fileData) throws Exception {
        if (fileData == null || fileData.length == 0) {
            throw new CustomException("fileData不能为空");
        }
        return uploadFile(enumUploadItem, fileData, enumUploadItem.getFileType());
    }

    public String uploadFile(EnumUploadItem enumUploadItem, byte[] fileData, String fileType) throws Exception {
        JGAssert.notNull(enumUploadItem, "enumUploadItem不能为空");
        JGAssert.hasText(fileType, "文件类型不能为空");
        JGAssert.isTrue(enumUploadItem.getMaxSize() >= fileData.length, "文件超过上传大小限制" + enumUploadItem.getMaxSize() / 1024 / 1024 + "M");
        String[] names = fileType.split("\\.");
        fileType = fileType.contains(".") ? names[names.length - 1] : fileType;
        String key = wrapKey(enumUploadItem, StringUtil.getUUID(), fileType);
        return uploadFileBytes(key, fileData);
    }


    public String uploadFile(String key, byte[] bytes) throws Exception {
        key = checkKey(key);
        if (fileExist(key)) {
            return key;
        }

        return uploadFileBytes(key, bytes);
    }


    /**
     * 组装oss的key
     */
    private String wrapKey(EnumUploadItem enumUploadItem, String fileName, String fileType) {
        String dateDir = DateHandler.translateToString(new Date(), 5);
        return PATH_SPLIT + PATH_PREFIX + enumUploadItem.getBaseDir() + PATH_SPLIT + dateDir + PATH_SPLIT + fileName + "." + fileType;
    }


    /**
     * 根据文件路径，获取再oss的key
     *
     * @param file 文件
     * @return byte[]
     */

    byte[] fileToByte(File file) throws IOException {
        FileInputStream inputStream = new FileInputStream(file);
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        byte[] temp = new byte[1024];
        int n;
        while ((n = inputStream.read(temp)) != -1) {
            byteArrayOutputStream.write(temp, 0, n);
        }
        inputStream.close();
        byteArrayOutputStream.close();
        return byteArrayOutputStream.toByteArray();
    }

    /**
     * 获取MD5码
     *
     * @param path 文件路径
     * @return md5
     */
    public abstract String getMD5String(String path);

    /**
     * 判断文件是不存在
     *
     * @param key 包含"/", 如：/oss/upload/facePicture/2019-06-27/22a67c2539bc422098019f3cf04c5ae0.jpg
     * @return boolean
     */
    public abstract boolean fileExist(String key);

    /**
     * 上传文件到对应的key(所有的上传方法都会转到该方法)
     *
     * @param key   包含"/", 如：/oss/upload/facePicture/2019-06-27/22a67c2539bc422098019f3cf04c5ae0.jpg
     * @param bytes 字节文件
     * @return 文件相对路径包含“/”，如：/oss/upload/facePicture/2019-06-27/22a67c2539bc422098019f3cf04c5ae0.jpg
     * @throws IOException 异常
     */
    protected abstract String uploadFileBytes(String key, byte[] bytes) throws IOException;

    /**
     * 通过文件路径获取文件字节
     *
     * @throws IOException 异常
     */
    protected abstract byte[] realGetFileBytes(String key) throws IOException;

    String checkKey(String key) {
        if (!key.startsWith("/oss")) {
            throw new CustomException("路径不合法");
        }
        return filterSpecialChar(key);
    }

    /**
     * 根据文件路径，获取在oss的key
     *
     * @param path 文件路径
     * @return oss中对应的文件key
     */

    String getOssKey(String path) {
        if (path.startsWith(PATH_SPLIT)) {
            path = path.substring(PATH_SPLIT.length());
        }
        return path;

    }

    /**
     * 组装固定key,（现用于组装特征值文件路径）
     *
     * @return
     */
    public String getFileKey(EnumUploadItem enumUploadItem, String... paths) {
        JGAssert.notNull(enumUploadItem, "enumUploadItem不能为空");
        if (paths == null || paths.length == 0) {
            throw new CustomException("paths路径不能为空");
        }
        String key = PATH_SPLIT + PATH_PREFIX + enumUploadItem.getBaseDir();
        for (String path : paths) {
            if (StringUtils.isEmpty(path)) {
                throw new CustomException("paths路径不能存在空路径");
            }
            key = key + PATH_SPLIT + path;
        }
        return key;
    }

    /**
     * 对路径特殊参数进行过滤并获取文件字节，
     *
     * @param path
     * @return
     */
    public byte[] getFileBytes(String path) throws IOException {
        JGAssert.hasText(path, "path不能为空");
        return realGetFileBytes(filterSpecialChar(path));
    }

    //过滤特殊字符
    private String filterSpecialChar(String path) {
        String[] specialChar = {"?", "&", "@", "%", "!", "*"};
        for (String s : specialChar) {
            if (path.contains(s)) {
                path = path.split(s)[0];
            }
        }
        return path;
    }

    /**
     * 文件转换目录
     */
    public String changeDir(EnumUploadItem enumUploadItem, String key) throws Exception {
        return uploadFile(enumUploadItem, getFileBytes(key), key);
    }
}
